#include "modesto.h"
#include <iostream>

Modesto::Modesto() : IRCEvents()
{
	this->servers		= ServerList();
	this->status		= STOPPED;
	this->connections	= 0;
}

Modesto::Modesto(std::string configFile) : IRCEvents()
{
	if (!configFile.empty())
	{
		// This is where we will parse the config file.
		// Preferably using a library that allows for JSON, XML, and INI
	}

	this->servers	= ServerList();
}

// Open a new thread that will listen and queue all received messages.
void Modesto::startListening()
{
	this->listener = new Worker(this->listen);
	this->listener->setFlags(DETACHED_PROCESS);
	this->listener->addArgument(0, this);

	this->queue = new CommandQueue();

	if (!this->listener->start())
	{
		this->status = STOPPED;
	}
}

DWORD Modesto::listen(LPVOID *args)
{
	Modesto *modesto = reinterpret_cast<Modesto *>(args[0]);

	while (modesto->isRunning())
	{
		if (modesto->availableSockets() > 0)
		{
			modesto->receive();
		}
	}

	return 0;
}

// Connect to all the servers
bool Modesto::connect()
{
	WORD WSAVersion = MAKEWORD(2, 2);
	WSADATA wsaData;

	if (WSAStartup(WSAVersion, &wsaData) != 0)
	{
		return false;
	}

	this->connections = 0;

	for (ServerListIter serverIter = this->servers.begin(); serverIter != this->servers.end(); ++serverIter)
	{
		if (!serverIter->second->connect())
		{
			for (;serverIter != this->servers.begin(); serverIter--)
			{
				serverIter->second->disconnect();
				this->connections--;
			}

			return false;
		}

		this->connections++;
	}

	this->status = RUNNING;

	return true;
}

// Connect to a specific server.
bool Modesto::addConnection(ServerInfo info)
{
	if (info.address.empty())
	{
		return false;
	}

	if (reinterpret_cast<int>(info.port.c_str()) <= 0)
	{
		info.port = DEFAULT_PORT;
	}

	if (info.nick.empty())
	{
		info.nick = DEFAULT_NICK;
	}

	Server *server = new Server(info);

	this->servers.insert(ServerListItem(this->servers.size(), server));

	return true;
}

// Send a message to the server.
void Modesto::send(Server *server, std::string message)
{
	server->send(message);

	return;
}

// Send a message to a specific channel.
void Modesto::send(Server *server, Channel *channel, std::string message)
{
	this->send(server, channel->getName() + " " + message);

	return;
}

// Send a message to a specific user.
void Modesto::send(Server *server, User *user, std::string message)
{
	this->send(server, user->nick + message);

	return;
}

// Receive messages from the server, and place them into the queue.
void Modesto::receive()
{
	// TODO - Implement CommandQueue
	// All messages should be converted to a RawCmd and added to the command queue.
	if (this->queue->wait(INFINITE) == WAIT_OBJECT_0)
	{
		std::string msg;
		for (ServerListIter it = this->servers.begin(); it != this->servers.end(); ++it)
		{
			if (FD_ISSET(it->second->getConnection(), &this->readableSockets))
			{
				msg = it->second->receive(2048);
				if(msg.length() > 0)
				{
					if (msg.find("\r\n") != std::string::npos)
					{
						size_t pos1, pos2;
						std::string line;
						pos1 = 0;
						pos2 = 0;

						while ((pos1 = msg.find("\r\n", pos2)) != std::string::npos)
						{
							line = msg.substr(pos2, pos1 - pos2);
							pos2 = pos1 + 2;

							RawCmd cmd = RawCmd(it->first, line);
							this->queue->addCommand(cmd);

							//this->executeCommand(cmd);
						}
					}
					else
					{
						RawCmd cmd = RawCmd(it->first, msg);
						this->queue->addCommand(cmd);

						//this->executeCommand(cmd);
					}
				}
			}
		}
	}

	this->queue->release();
}

// Check whether the bot is still running.
bool Modesto::isRunning()
{
	if (this->status == STOPPED)
	{
		return false;
	}

	if (this->connections <= 0)
	{
		return false;
	}

	return true;
}

// Execute a command.
bool Modesto::executeCommand(RawCmd cmd)
{
	debugcommand(cmd);

	ServerListIter it;
	if ((it = this->servers.find(cmd.server)) == this->servers.end())
	{
		return false;
	}

	Server *server = it->second;

	if (cmd.command == "PING")
	{
		server->send("PONG :" + cmd.params[0]);

		return true;
	}

	if (cmd.command == "JOIN")
	{
		return true;
	}

	if (cmd.command == "PRIVMSG" && cmd.isValid)
	{
		if (cmd.params[0].substr(0,1) == "#")
		{
			Channel *channel	= server->getChannel(cmd.params[0]);
			User *user		= server->getUser(cmd.originator.parts[0]);
			this->onMessage(server, channel, user, cmd.params);
		}
		else
		{
			User *user = server->getUser(cmd.originator.parts[0]);
			this->onMessage(server, user, cmd.params);
		}

		return true;
	}

	if (cmd.command == "352")
	{
		if (server->getServerInfo().nick != cmd.params[5])
		{
			if (!server->hasUser(cmd.params[5]))
			{
				User *user = new User();
				user->nick = cmd.params[5];
				user->host = cmd.params[3];

				server->addUser(user);
			}
		}

		return true;
	}

	if (cmd.command == "353")
	{
		std::cout <<  "WHO " + cmd.params[2] << std::endl;

		server->send("WHO " + cmd.params[2]);

		Channel *channel = server->addChannel(cmd.params[2]);
		this->onJoin(server, channel);

		return true;
	}

	if (cmd.command == "001")
	{
		this->onConnect(server);

		return true;
	}

	if (cmd.command == "ERROR")
	{
		server->disconnect();
		this->connections--;
	}

	return true;
}

Queue Modesto::fetchQueue()
{
	return this->queue->getQueue();
}

bool Modesto::loadModules()
{
	return false;
}

void Modesto::unloadModules()
{
	return;
}

void Modesto::terminate()
{
	WSACleanup();
}

int Modesto::availableSockets()
{
	FD_ZERO(&this->readableSockets);
	FD_ZERO(&this->writableSockets);

	SOCKET sock;

	for (ServerListIter it = this->servers.begin(); it != this->servers.end(); ++it)
	{
		sock = it->second->getConnection();

		FD_SET(sock, &this->readableSockets);
		FD_SET(sock, &this->writableSockets);
	}

	return select(NULL, &this->readableSockets, &this->writableSockets, NULL, 0);
}

void Modesto::userJoinChannel(Channel *channel, User *user)
{
	return;
}

void Modesto::userQuit(Server *server, User *user)
{
	return;
}

void Modesto::onConnect(Server *server)
{
	ServerInfo info = server->getServerInfo();

	for (std::vector<std::string>::iterator it = info.channels.begin(); it != info.channels.end(); ++it)
	{
		server->send("JOIN " + *it);
	}
}

void Modesto::onDisconnect(Server *server)
{

}

void Modesto::onJoin(Server *server, Channel *channel)
{
	server->send("PRIVMSG " + channel->getName() + " Hello!");
}

void Modesto::onJoin(Server *server, Channel *channel, User *user)
{

}

void Modesto::onPart(Server *server, Channel *channel, User *user)
{

}

void Modesto::onNick(Server *server, User *user, std::string nick)
{

}

void Modesto::onMessage(Server *server, User *user, std::vector<std::string> params)
{
	std::stringstream ss;

	if (params.size() > 1)
	{
		if (params[1] == "!join")
		{
			if (params.size() > 2)
			{
				server->send("JOIN " + params[2]);
			}
		}
		else if (params[1] == "!part")
		{
			if (params.size() > 2)
			{
				server->send("PART " + params[2]);
			}
		}
		else if (params[1] == "!list")
		{
			if (params.size() > 2)
			{
				if (params[2] == "channels")
				{
					ss << "PRIVMSG " + params[0] + " I'm currently in ";

					ChannelList list = server->getChannelList();

					for (ChannelListIter it = list.begin(); it != list.end(); ++it)
					{
						ss << it->second->getName();

						if (it != --list.end())
						{
							ss << ", ";
						}
					}
				}
				else if (params[2] == "users")
				{
					UserList list = server->getUserList();

					ss << "PRIVMSG " << params[0] << " There are " << list.size() << " users in " << params[0] << ": ";

					for (UserListIter it = list.begin(); it != list.end(); ++it)
					{
						ss << it->second->nick;

						if (it != --list.end())
						{
							ss << ", ";
						}
					}
				}

				std::cout << ss.str();
			}
		}
		else if (params[1] == "!say")
		{
			if (params.size() > 3)
			{
				ss << "PRIVMSG " + params[2] << " :";

				for (unsigned int i = 3; i < params.size(); i++)
				{
					if (i == params.size() - 1)
					{
						ss << params[i];
						break;
					}

					ss << params[i] << " ";
				}
			}
		}
		else if (params[1] == "!quit")
		{
			ss << "QUIT :";

			if (params.size() > 2)
			{
				for (unsigned int i = 2; i < params.size(); i++)
				{
					if (i == params.size() - 1)
					{
						ss << params[i];
						break;
					}

					ss << params[i] << " ";
				}
			}
			else
			{
				ss << "Goodbye";
			}

			server->send(ss.str().c_str());
			server->disconnect();

			this->connections--;

			return;
		}
	}

	server->send(ss.str());
}

void Modesto::onMessage(Server *server, Channel *channel, User *user, std::vector<std::string> params)
{
	this->onMessage(server, user, params);
}

void Modesto::onNotice(Server *server, std::string notice)
{

}

void debugcommand(RawCmd cmd)
{
	std::cout << std::endl;
	std::cout << "Command: " << cmd.command << std::endl;
	std::cout << "Originator: " << (cmd.originator.isUser ? cmd.originator.parts["nick"] : cmd.originator.parts["host"]) << std::endl;
	std::cout << "Parameters: ";

	int size = cmd.params.size();
	for (int i = 0; i < size; i++)
	{
		std::cout << cmd.params[i];

		if (i < size - 1)
		{
			std::cout << ", ";
		}
		else
		{
			std::cout << std::endl;
		}
	}

	std::cout << std::endl << "-----------------------------------------" << std::endl;
}